'use strict';

/**
 * @param str
 * @param operation
 * @param key
 * @param expiry
 * @return string 加密串
 */
function authcode(str, operation, key, expiry) {
    let j;
    let i;
    let keyC;
    let strBuf;
    let tmp;
    let tmpStr;
    let s = '';
    operation = operation ? operation : 'DECODE';
    key = key ? key : '';
    expiry = expiry ? expiry : 0;
    const keyCLen = 4;
    key = md5(key);
    // 密匙a会参与加解密
    const keyA = md5(key.slice(0, 16));
    // 密匙b会用来做数据完整性验证
    const keyB = md5(key.slice(16, 32));
    // 密匙c用于变化生成的密文
    // IE下不支持substr第一个参数为负数的情况
    if (keyCLen) {
        if (operation === 'DECODE') {
            keyC = str.slice(0, keyCLen);
        } else {
            const md5_time = md5(microTime());
            const start = md5_time.length - keyCLen;
            keyC = md5_time.slice(start, start + keyCLen);
        }
    } else {
        keyC = '';
    }
    // 参与运算的密匙
    const cryptKey = keyA + md5(keyA + keyC);
    if (operation === 'DECODE') {
        str = str.slice(keyCLen);
        strBuf = Base64.decode(str);
    } else {
        expiry = expiry ? expiry + time() : 0;
        tmpStr = expiry.toString();
        if (tmpStr.length >= 10) {
            str = tmpStr.slice(0, 10) + md5(str + keyB).slice(0, 16) + str;
        } else {
            const count = 10 - tmpStr.length;
            for (i = 0; i < count; i++) {
                tmpStr = '0' + tmpStr;
            }
            str = tmpStr + md5(str + keyB).slice(0, 16) + str;
        }
        strBuf = str;
    }
    const box = new Array(256);
    for (i = 0; i < 256; i++) {
        box[i] = i;
    }
    const rndKey = [];
    // 产生密匙簿
    for (i = 0; i < 256; i++) {
        rndKey[i] = cryptKey.charCodeAt(i % cryptKey.length);
    }
    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
    for (j = i = 0; i < 256; i++) {
        j = (j + box[i] + rndKey[i]) % 256;
        tmp = box[i];
        box[i] = box[j];
        box[j] = tmp;
    }
    // 核心加解密部分
    //IE下不支持直接通过下标访问字符串的字符，需要先转换为数组
    strBuf = strBuf.split('');
    for (let a = j = i = 0; i < strBuf.length; i++) {
        a = (a + 1) % 256;
        j = (j + box[a]) % 256;
        tmp = box[a];
        box[a] = box[j];
        box[j] = tmp;
        // 从密匙簿得出密匙进行异或，再转成字符
        s += chr(ord(strBuf[i]) ^ (box[(box[a] + box[j]) % 256]));
    }
    if (operation === 'DECODE') {
        if ((s.slice(0, 10) === '0000000000' || s.slice(0, 10) - time() > 0) && s.slice(10, 26) === md5(s.slice(26) + keyB).slice(0, 16)) {
            s = s.slice(26);
        } else {
            s = '';
        }
    } else {
        s = Base64.encode(s);
        const regex = new RegExp('=', "g");
        s = s.replace(regex, '');
        s = keyC + s;
    }
    return s;
}

function time() {
    const ms = new Date().getTime();
    return Math.ceil(ms / 1000);
}

function microTime(get_as_float) {
    const ms = new Date().getTime();
    const sec = Math.ceil(ms / 1000);
    return get_as_float ? (ms / 1000) : (ms - (sec * 1000)) / 1000 + ' ' + sec;
}

function chr(s) {
    return String.fromCharCode(s);
}

function ord(s) {
    return s.charCodeAt(0);
}
